import * as THREE from 'three';
import { SketchBase } from "fast-three-sketch";
import { InputManager } from "./InputManager";
import { CameraManager } from "./CameraManager";
import { assetManager, AssetManager } from "./AssetManager";
import { City } from "./simulation";
import { SimObject } from "./simulation";
import { UIManager } from './UIManager';


export class Game extends SketchBase {

    isPaused: boolean = false;

    activeToolId: string = "select";

    focusedObject: SimObject = null;

    selectedObject: SimObject = null;

    inputManager: InputManager;

    cameraManager: CameraManager;

    assetManager: AssetManager;

    uiManager: UIManager;

    private _raycaster: THREE.Raycaster;

    city: City;

    constructor(targetElement: HTMLElement) {

        super(targetElement, { alpha: true });

        this.camera.layers.enable(1);

        this.inputManager = new InputManager(this);
        this.cameraManager = new CameraManager(this);
        this.assetManager = assetManager;
        assetManager.onLoad = this.onload;
        this.uiManager = new UIManager(this);

        this.focusedObject = null;

        this._raycaster = new THREE.Raycaster();

        this.city = new City(this);

    }

    private onload = () => {

        this.uiManager.hideLoadingText();
        this.inputManager.add("mousedown", this.useTool);
        this.inputManager.add("mousemove", this.updateFocusedObject);

        this.city.setSize(16);

        this.initialize(this.city);
        this.start();

        setInterval(this.simulate.bind(this), 1000);

    };

    initialize(city: City) {

        this.add(city);

        this.setupLights();
        this.setupGrid(city);

    }

    /** 开始渲染 */
    start() {
        this.renderer.setAnimationLoop(this.draw.bind(this));
    }


    /** 停止渲染 */
    stop() {
        this.renderer.setAnimationLoop(null);
    }


    /** 渲染 */
    draw() {
        this.city.draw();
        this.renderer.render(this.scene, this.camera);
    }

    /** 使用工具栏 */
    private useTool = () => {

        if (this.inputManager.isLeftMouseDown) {

            const toolId = this.activeToolId;

            switch (toolId) {

                case 'select':
                    this.updateSelectedObject();
                    this.uiManager.updateInfoPanel(this.selectedObject);
                    break;
                case 'bulldoze':
                    if (this.focusedObject) {
                        const { x, y } = this.focusedObject;
                        this.city.bulldoze(x, y);
                    }
                    break;
                default:
                    if (this.focusedObject) {
                        const { x, y } = this.focusedObject;
                        this.city.placeBuilding(x, y, toolId);
                    }
                    break;
            }
        }

    };

    /** 更新选中对象并且设置高亮 */
    updateSelectedObject() {
        this.selectedObject?.setSelected(false);
        this.selectedObject = this.focusedObject;
        this.selectedObject?.setSelected(true);
    }

    /** 设置当前聚焦对象 */
    private updateFocusedObject = (event: MouseEvent) => {

        if (this.focusedObject) {
            this.focusedObject.setFocused(false);
        }

        const newObject = this._raycast(event);

        if (newObject !== this.focusedObject) {
            this.focusedObject = newObject;
        }

        this.focusedObject?.setFocused(true);

    };

    /** 射线检测 */
    private _raycast(event: MouseEvent) {

        const { left, top, width, height } = this.targetElement.getBoundingClientRect();

        const mouse = new THREE.Vector2();

        mouse.x = ((event.clientX - left) / width) * 2 - 1;
        mouse.y = -((event.clientY - top) / height) * 2 + 1;

        const raycaster = this._raycaster;

        raycaster.setFromCamera(mouse, this.camera);

        const intersections = raycaster.intersectObjects(this.city.root.children, true);

        if (intersections.length) {

            return intersections[0].object.userData.simObject;

        } else {

            return null;

        }

    }

    /** 模拟 */
    simulate() {
        if (this.isPaused) return;

        /** 更新城市数据模型 */
        this.city.simulate(1);

        this.uiManager.updateTitleBar(this);
        this.uiManager.updateInfoPanel(this.selectedObject);
    }

    private setupLights() {
        const sun = new THREE.DirectionalLight(0xffffff, 2);
        sun.position.set(-10, 20, 0);
        sun.castShadow = true;
        sun.shadow.camera.left = -20;
        sun.shadow.camera.right = 20;
        sun.shadow.camera.top = 20;
        sun.shadow.camera.bottom = -20;
        sun.shadow.mapSize.width = 2048;
        sun.shadow.mapSize.height = 2048;
        sun.shadow.camera.near = 10;
        sun.shadow.camera.far = 50;
        sun.shadow.normalBias = 0.01;
        this.add(sun);
        this.add(new THREE.AmbientLight(0xffffff, 0.5));
    }

    private setupGrid(city: City) {
        const gridMaterial = new THREE.MeshBasicMaterial({
            color: 0x000000,
            map: assetManager.textures['grid'],
            transparent: true,
            opacity: 0.2
        });
        gridMaterial.map.repeat = new THREE.Vector2(city.size, city.size);
        gridMaterial.map.wrapS = gridMaterial.map.wrapT = THREE.RepeatWrapping;

        const grid = new THREE.Mesh(
            new THREE.BoxGeometry(city.size, 0.1, city.size),
            gridMaterial
        );
        grid.position.set(city.size / 2 - 0.5, -0.04, city.size / 2 - 0.5);
        this.scene.add(grid);
    }
}